<template>
    <div>

        <!-- 翻页控制区 -->
        <div class="controls">
            <button @click="prevPage" :disabled="currentPage <= 1">上一页</button>
            <span>第 {{ currentPage }} 页 / 共 {{ totalPages }} 页</span>
            <button @click="nextPage" :disabled="currentPage >= totalPages">下一页</button>

            &nbsp;&nbsp;

            <!-- 缩放按钮 -->
            <button @click="zoomOut" :disabled="scale <= 0.5">-</button>
            <span>缩放: {{ (scale * 100).toFixed(0) }}%</span>
            <button @click="zoomIn" :disabled="scale >= 3.0">+</button>
        </div>

        <canvas ref="pdfCanvas" style="width: 100%; border: 1px solid #ccc;"></canvas>
    </div>
</template>

<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';
import { getDocument, GlobalWorkerOptions, version, PDFDocumentProxy, RenderTask } from 'pdfjs-dist';
import 'pdfjs-dist/web/pdf_viewer.css';

GlobalWorkerOptions.workerSrc = '../../node_modules/pdfjs-dist/build/pdf.worker.mjs';

const props = defineProps({
    fileUrl: {
        type: String,
        required: true,
    },
});
console.log('PdfViewer received fileUrl:', props.fileUrl); // 添加日志
const pdfCanvas = ref<HTMLCanvasElement | null>(null);

// 存储 PDF 文档对象
//const pdfDoc = ref<pdfjsLib.PDFDocumentProxy | null>(null);
let pdfDoc: any = ""; // 文档内容---必须使用非响应式存储
// 当前页码，初始为 1
const currentPage = ref<number>(1);

// PDF 文档的总页数
const totalPages = ref<number>(0);

// 缩放比例，可以根据需要调整
const scale = ref<number>(1.5);

// 定义页面缓存，Map 的键是页码，值是 off-screen canvas
const pageCache = ref<Map<number, HTMLCanvasElement>>(new Map());

// 定义缓存的最大容量
const MAX_CACHE_SIZE = 10;

// 跟踪当前的渲染任务
const currentRenderTask = ref<RenderTask | null>(null);
//渲染锁
const isRendering = ref<boolean>(false);

/**
 * 加载 PDF 文档并初始化相关数据
 * @param url PDF 文件的 URL
 */
const loadPdf = async (url: string) => {
    try {
        // 异步加载 PDF 文档
        const loadingTask = getDocument(url);
        const pdf = await loadingTask.promise;
        // 存储 PDF 文档对象
        pdfDoc = pdf;
        // 获取总页数
        totalPages.value = pdf.numPages;
        // 渲染第一页
        await renderPage(currentPage.value);
    } catch (error) {
        console.error('加载 PDF 时出错:', error);
    }
};
//从缓存中加载页面
const loadPageFromCache = async (pageNum: number) => {
    if (!pdfDoc || !pdfCanvas.value) return;

    // 检查缓存中是否已有该页的渲染结果
    if (pageCache.value.has(pageNum)) {
        console.log(`从缓存中加载第 ${pageNum} 页`);
        const cachedCanvas = pageCache.value.get(pageNum);
        if (cachedCanvas && pdfCanvas.value) {
            const context = pdfCanvas.value.getContext('2d');
            if (context && cachedCanvas) {
                // 设置主 canvas 的尺寸
                pdfCanvas.value.width = cachedCanvas.width;
                pdfCanvas.value.height = cachedCanvas.height;
                pdfCanvas.value.style.width = `${cachedCanvas.width / window.devicePixelRatio}px`;
                pdfCanvas.value.style.height = `${cachedCanvas.height / window.devicePixelRatio}px`;

                // 清空主 canvas
                context.clearRect(0, 0, pdfCanvas.value.width, pdfCanvas.value.height);

                // 绘制缓存的内容到主 canvas
                context.drawImage(cachedCanvas, 0, 0);
            }
        }
        return;
    }
}

//重新渲染页面
const reRenderPage = async (pageNum: number) => {
    if (!pdfDoc || !pdfCanvas.value) return;
    try {
        // 如果有正在进行的渲染任务，取消它
        if (currentRenderTask.value) {
            console.log('取消之前的渲染任务');
            currentRenderTask.value.cancel();
        }
        // 设置渲染锁
        isRendering.value = true;

        // 获取指定页码的页面对象
        const page = await pdfDoc.getPage(pageNum);
        // 设置视图端口（viewport）
        const viewport = page.getViewport({ scale: scale.value });
        // 获取设备像素比，以支持高分辨率屏幕
        const outputScale = window.devicePixelRatio || 1;

        const canvas = pdfCanvas.value;
        const context = canvas.getContext('2d');
        if (context) {
            // 设置 canvas 的宽高，考虑设备像素比
            canvas.width = Math.floor(viewport.width * outputScale);
            canvas.height = Math.floor(viewport.height * outputScale);
            // 设置 canvas 在页面上的显示尺寸
            canvas.style.width = `${Math.floor(viewport.width)}px`;
            canvas.style.height = `${Math.floor(viewport.height)}px`;

            // 如果输出缩放不为 1，则应用转换矩阵
            const transform = outputScale !== 1
                ? [outputScale, 0, 0, outputScale, 0, 0]
                : undefined;

            // 渲染上下文配置
            const renderContext = {
                canvasContext: context,
                transform,
                viewport,
            };


            // 开始渲染页面，并跟踪渲染任务
            const renderTask = page.render(renderContext);
            currentRenderTask.value = renderTask;

            await renderTask.promise;

            // 渲染完成后，清除当前渲染任务
            currentRenderTask.value = null;

            // 将渲染结果存入缓存
            const cachedCanvas = document.createElement('canvas');
            cachedCanvas.width = canvas.width;
            cachedCanvas.height = canvas.height;
            const cachedContext = cachedCanvas.getContext('2d');
            if (cachedContext) {
                cachedContext.drawImage(canvas, 0, 0);
                pageCache.value.set(pageNum, cachedCanvas);
                console.log(`将第 ${pageNum} 页渲染结果存入缓存`);
                // 检查缓存是否超过最大容量
                clearOldestCache();
            }
        }
    } catch (error: any) {
        if (error.name === 'RenderingCancelledException') {
            console.log('渲染任务被取消');
        } else {
            console.error('渲染页面时出错:', error);
        }
    } finally {
        // 释放渲染锁
        isRendering.value = false;
    }
}


/**
 * 渲染指定页码的 PDF 页面
 * @param pageNum 页码
 */
const renderPage = async (pageNum: number) => {
    if (!pdfDoc || !pdfCanvas.value) return;

    // 如果正在渲染，跳过此次渲染
    if (isRendering.value) {
        console.log('渲染任务正在进行中，跳过此次渲染');
        return;
    }
    // 检查缓存中是否已有该页的渲染结果
    loadPageFromCache(pageNum);

    //重新渲染页面
    reRenderPage(pageNum);
    
};

/**
 * 加载指定页码的 PDF 页面
 */
const loadPage = async () => {
    if (pdfDoc) {
        await renderPage(currentPage.value);
    }
};

/**
 * 清除最早缓存的页面
 */
const clearOldestCache = () => {
    if (pageCache.value.size > MAX_CACHE_SIZE) {
        // 获取最早添加的键（页码）
        const firstKey = pageCache.value.keys().next().value;
        if (firstKey !== undefined) {
            pageCache.value.delete(firstKey);
            console.log(`清除缓存中的第 ${firstKey} 页`);
        }
    }
};

// 在组件挂载后加载 PDF
onMounted(() => {
    if (props.fileUrl) {
        loadPdf(props.fileUrl);
    }
});

// 监听 fileUrl 的变化，重新加载 PDF
watch(
    () => props.fileUrl,
    (newUrl) => {
        if (newUrl) {
            // 重置当前页码
            currentPage.value = 1;
            // 清空缓存
            pageCache.value.clear();
            loadPdf(newUrl);
        }
    }
);

/**
 * 跳转到下一页
 */
const nextPage = async () => {
    if (currentPage.value < totalPages.value) {
        currentPage.value += 1;
        await loadPage();
    }
};
/**
 * 跳转到上一页
 */
const prevPage = async () => {
    if (currentPage.value > 1) {
        currentPage.value -= 1;
        await loadPage();
    }
};
/**
 * 放大页面
 */
const zoomIn = () => {
    // 设置最大缩放比例为 3.0
    if (scale.value < 3.0) {
        scale.value += 0.25;
    }
};

/**
 * 缩小页面
 */
const zoomOut = () => {
    // 设置最小缩放比例为 0.5
    if (scale.value > 0.5) {
        scale.value -= 0.25;
    }
};

// 不用监听 currentPage 的变化，因为在“上一页”和“下一页”按钮的点击事件中已经调用了 loadPage 方法
//需要监听scale 的变化，重新渲染页面
watch([scale], async () => {
    console.log('监听到页面缩放比例变化');
    // 重新渲染当前页，不要使用 loadPage 方法，因为它会检查缓存
    await renderPage(currentPage.value);
  
});
</script>

<style scoped>
/* 容器样式 */
.pdf-viewer-container {
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* 画布样式 */
canvas {
    width: 90%;
    border: 1px solid #ccc;
}

/* 控制区样式 */
.controls {
    margin-top: 10px;
    display: flex;
    align-items: center;
}


.controls span {
    font-size: 16px;
}

/* 按钮样式 */
.controls button {
    margin: 0 10px;
    padding: 5px 10px;
    cursor: pointer;
}

/* 禁用状态下按钮样式 */
.controls button:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

/* 页码和缩放比例文本样式 */
.controls span {
    font-size: 16px;
}
</style>